package com.youku.compiler;

import static com.youku.compiler.TokenType.*;

import java.util.ArrayList;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.youku.ast.ArgumentAst;
import com.youku.ast.AssignAst;
import com.youku.ast.Ast;
import com.youku.ast.BaseAst;
import com.youku.ast.BlockAst;
import com.youku.ast.BlockWithConditionAst;
import com.youku.ast.ClassBlockAst;
import com.youku.ast.DefClassAst;
import com.youku.ast.DefFunctionAst;
import com.youku.ast.DotAst;
import com.youku.ast.FourOpAst;
import com.youku.ast.IfStatementAst;
import com.youku.ast.InvokeFunctionAst;
import com.youku.ast.LogicAst;
import com.youku.ast.NegativeExpAst;
import com.youku.ast.NullStatementAst;
import com.youku.ast.PrimaryAst;
import com.youku.ast.RelationAst;
import com.youku.ast.WhileLoopAst;

public class Parser {

	private static final Logger logger = LoggerFactory.getLogger(Parser.class);

	private List<Token> tokens;

	// 存储所有的语法树
	private Ast root;

	private int pos;

	public Parser(Tokenizer tokenizer) {
		super();
		this.tokens = tokenizer.getTokens();
		this.root = new BaseAst();
	}

	/**
	 * bnf:
	 * 
	 * program:defClass|defFunction|statement
	 * 
	 * defFunction:"def" Identifier "(" arg ")" "{" (statement)* "}"
	 * 
	 * arg:[identifier(,identifier)*]
	 * 
	 * statement:ifstat|whilestate|forstate|assign|invoke
	 * 
	 * ifstat:"if" exp "{" (statement)* "}" ["else" exp] "end" assign:identifer
	 * "=" exp invoke:idenfier "(" arg ")"
	 */
	public void process() {
		while (hasMoreToken()) {
			Token token = peekToken();

			Ast ast = null;
			switch (token.tokenType) {
			case CLASS:
				ast = matchDefClass();
				break;
			case DEF:
				ast = matchDefFunction();
				break;
			default:
				ast = matchStatement();
				break;
			}
			root.addChild(ast);

		}

		logger.info(root.toString());

	}

	private Ast matchDefClass() {
		nextToken();// class
		Ast identifier = matchIdentifier();

		Ast parent = null;

		if (peekToken(EXTENDS)) {
			nextToken();

			parent = matchIdentifier();
		}

		return new DefClassAst(identifier, parent, matchClassBlock());
	}

	private Ast matchClassBlock() {
		assertNextToken("缺少左括号{", LBRACE);

		Ast classBlock = new ClassBlockAst();
		while (!peekToken(RBRACE)) {
			if (peekToken(DEF)) {
				classBlock.addChild(matchDefFunction());
			} else if (peekToken(IDENTIFIER)) {
				classBlock.addChild(matchDefVariable());
			} else {
				throw new RuntimeException("类中不存在该类型的符号" + peekToken());
			}
		}

		assertNextToken("缺少右括号}", RBRACE);
		return classBlock;
	}

	/**
	 * identifier=exp
	 * 
	 * @return
	 */
	private Ast matchDefVariable() {
		Ast var = matchIdentifier();
		assertNextToken("缺少赋值运算符'='", ASSIGN);
		Ast exp = matchLogicExp();

		return new AssignAst(var, exp);
	}

	private Ast matchDefFunction() {
		nextToken();// def

		Ast identifier = matchIdentifier();
		if (identifier == null)
			throw new RuntimeException("方法名不合法");

		Ast arg = matchDefArgument();
		if (arg == null)
			throw new RuntimeException("方法参数不合法");

		Ast block = matchBlock();
		if (block == null)
			throw new RuntimeException("方法语句块不合法");

		return new DefFunctionAst(identifier, arg, block);
	}

	/**
	 * 方法定义中的参数声明部分
	 * 
	 * @return
	 */
	private Ast matchDefArgument() {
		assertPeekToken("缺少左括号(", LPAREN);

		List<Ast> args = new ArrayList<>();

		do {
			nextToken();
			if (peekToken(RPAREN)) {
				break;
			} else {
				args.add(matchIdentifier());
			}
		} while (peekToken(COMMA));

		assertNextToken("缺少右括号)", RPAREN);

		return new ArgumentAst(args);
	}

	/**
	 * 调用方法中的参数使用部分
	 * 
	 * @return
	 */
	private Ast matchArgument() {
		assertPeekToken("缺少左括号(", LPAREN);

		List<Ast> args = new ArrayList<>();

		do {
			nextToken();
			if (peekToken(RPAREN)) {
				break;
			} else {
				args.add(matchLogicExp());
			}
		} while (peekToken(COMMA));

		assertNextToken("缺少右括号)", RPAREN);

		return new ArgumentAst(args);
	}

	private Ast matchBlock() {
		assertNextToken("缺少左括号{", LBRACE);

		if (peekToken(RBRACE)) {
			nextToken();
			return new BlockAst(new NullStatementAst());
		}

		List<Ast> asts = matchStatementList();

		assertNextToken("缺少右括号}", RBRACE);

		return new BlockAst(asts);
	}

	private List<Ast> matchStatementList() {
		List<Ast> stateList = new ArrayList<>();

		do {
			Ast stmt = matchStatement();
			if (stmt instanceof NullStatementAst)
				continue;
			else
				stateList.add(stmt);

			if (peekToken(SEMI))
				continue;

		} while (!peekToken(RBRACE));

		return stateList;
	}

	/**
	 * statement:ifstat|whilestate|forstate|assign|invoke
	 * 
	 * @return
	 */
	private Ast matchStatement() {
		if (peekToken(SEMI)) {
			nextToken();
			return new NullStatementAst();
		}

		switch (peekToken().tokenType) {
		case IF:
			return matchIfStatement();
		case WHILE:
			return matchWhileLoop();
		case FOR:
			return matchForLoop();
		default:
			return matchSentence();
		}
	}

	/**
	 * 匹配单个语句/赋值语句/函数调用语句
	 * 
	 * @return
	 */
	private Ast matchSentence() {
		Ast assign = matchLocalVariable();
		TokenType op = peekToken().tokenType;
		if (op == ASSIGN) {
			nextToken();
			assign = new AssignAst(assign, matchLogicExp());
		} else if (op == LPAREN) {
			assign = new InvokeFunctionAst(assign, matchArgument());
		}

		return assign;
	}

	private Ast matchForLoop() {
		return null;
	}

	private Ast matchWhileLoop() {
		Token whileToken = nextToken();

		Ast exp = matchExpWithParen();

		Ast block = matchBlock();

		return new WhileLoopAst(new BlockWithConditionAst(whileToken, exp, block));
	}

	private Ast matchIfStatement() {
		// if
		Ast ifStat = new IfStatementAst(new BlockWithConditionAst(nextToken(), matchExpWithParen(), matchBlock()));

		while (peekToken(ELSIF)) {
			ifStat.addChild(new BlockWithConditionAst(nextToken(), matchExpWithParen(), matchBlock()));
		}

		if (peekToken(ELSE)) {
			ifStat.addChild(new BlockWithConditionAst(nextToken(), null, matchBlock()));
		}
		return ifStat;
	}

	private Ast matchExpWithParen() {
		assertNextToken("缺少左括号(", LPAREN);

		Ast exp = matchLogicExp();

		assertNextToken("缺少右括号)", RPAREN);

		return exp;
	}

	private Ast matchIdentifier() {
		assertPeekToken("非法字符", IDENTIFIER);

		return new PrimaryAst(nextToken());
	}

	// private Ast matchExp() {
	// Ast assign = matchLocalVariable();
	// TokenType op = peekToken().tokenType;
	// if (op == ASSIGN) {
	// nextToken();
	// assign = new AssignAst(assign, matchLogicExp());
	// } else if (op == LPAREN) {
	// assign = new InvokeFunctionAst(assign, matchArgument());
	// }
	//
	// return assign;
	// }

	private Ast matchLocalVariable() {
		Ast var = matchIdentifier();
		while (peekToken(DOT)) {
			nextToken();
			var = new DotAst(var, matchIdentifier());
		}
		return var;
	}

	private Ast matchLogicExp() {
		Ast ast = matchRelationExp();
		while (peekToken(AND, OR)) {
			Token op = nextToken();

			Ast opAst = matchRelationExp();

			ast = new LogicAst(op, ast, opAst);
		}

		return ast;
	}

	private Ast matchRelationExp() {
		Ast sumExp = matchNumExp();
		if (peekToken(EQ, NE, LT, GT, LE, GE)) {
			Token op = nextToken();

			Ast sumedExp = matchNumExp();

			sumExp = new RelationAst(op, sumExp, sumedExp);
		}

		return sumExp;
	}

	private Ast matchNumExp() {
		Ast multiExp = matchMultiExp();
		if (peekToken(PLUS, MINUS)) {
			Token op = nextToken();

			Ast multiedExp = matchMultiExp();

			multiExp = new FourOpAst(op, multiExp, multiedExp);
		}

		return multiExp;
	}

	private Ast matchMultiExp() {
		Ast p1 = matchPrimaryExp();
		if (peekToken(MULTIPLY, DIVIDE)) {
			Token op = nextToken();

			Ast p2 = matchPrimaryExp();

			p1 = new FourOpAst(op, p1, p2);
		}

		return p1;
	}

	/**
	 * primary:identifier|"(" exp ")"|基本数据类型和字符串
	 * 
	 * @return
	 */
	private Ast matchPrimaryExp() {
		switch (peekToken().tokenType) {
		case IDENTIFIER:
			return matchLocalVariable();
		case LPAREN:
			nextToken();
			Ast exp = matchLogicExp();
			assertNextToken("缺少右括号)", RPAREN);
			return exp;
		case STRING:
		case INT:
		case FLOAT:
		case TRUE:
		case FALSE:
			return new PrimaryAst(nextToken());
		case MINUS:
			nextToken();
			return new NegativeExpAst(matchLogicExp());
		default:
			throw new RuntimeException("基本数据中不存在" + peekToken());
		}
	}

	private boolean peekToken(TokenType... tokenTypes) {
		if (!hasMoreToken())
			return false;

		Token token = peekToken();

		for (TokenType type : tokenTypes) {
			if (type == token.tokenType)
				return true;
		}

		return false;
	}

	private boolean nextToken(TokenType... tokenTypes) {
		if (!hasMoreToken())
			return false;

		Token token = nextToken();

		for (TokenType type : tokenTypes) {
			if (type == token.tokenType)
				return true;
		}

		return false;
	}

	public Token nextToken() {
		return tokens.get(pos++);
	}

	public Token peekToken() {
		return tokens.get(pos);
	}

	public void assertNextToken(String errorMsg, TokenType... tokenTypes) {
		if (!nextToken(tokenTypes))
			throw new RuntimeException(errorMsg);
	}

	public void assertPeekToken(String errorMsg, TokenType... tokenTypes) {
		if (!peekToken(tokenTypes))
			throw new RuntimeException(errorMsg);
	}

	private boolean hasMoreToken() {
		return pos < tokens.size();
	}

	public Ast tree() {
		return root;
	}

}
